package com.mayflygeek.mayflyask.framework.webmvc.interceptor;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mayflygeek.mayflyask.framework.webmvc.interceptor.MethodHelper.MethodAndAnno;

/**
 * @author 虫虫
 * @team 蜉蝣极客
 * @link www.mayflygeek.com
 * @Emial chongchong@mayflygeek.com
 * 
 */
public abstract class AbstractAnnotatedMethodHandler<A extends Annotation> implements AnnotatedMethodHandler<A> {
	private static final Logger logger = LoggerFactory.getLogger(AbstractAnnotatedMethodHandler.class);
	static Map<Key, List<Method>> methodsCache = Maps.newHashMap();

	@Override
	public void handler(Object controller, Object[] parameterObjects) {
		final List<Method> annotatedMethods = getAnnotatedMethods(controller.getClass());
		if (annotatedMethods == null || annotatedMethods.isEmpty()) {
			return;
		}

		for (Method method : annotatedMethods) {
			final Object[] parameters = MethodHelper.getParameterObjects(method.getParameterTypes(), parameterObjects);
			Object result = null;
			try {
				result = ReflectionUtils.invokeMethod(method, controller, parameters);
			} catch (Exception e) {
				logger.warn("执行" + method.getName() + "出错!", e);
			}
			logger.debug("method: " + method.getName() + ",执行结果: " + result);
		}
	}

	protected List<Method> getAnnotatedMethods(Class<?> controllerType) {
		Key key = new Key(controllerType, getMethodAnnotation());
		List<Method> annotatedMethods = methodsCache.get(key);
		if (annotatedMethods == null) {
			List<MethodAndAnno<A>> methodAndAnnos = MethodHelper.findMethodAndAnnos(controllerType,
					getMethodAnnotation(), getSupportedInjectParameterTypes());
			annotatedMethods = Lists.newArrayList();

			for (MethodAndAnno<A> methodAndAnno : methodAndAnnos) {
				annotatedMethods.add(methodAndAnno.getMethod());
			}
			logger.debug(key.toString() + ", method: " + annotatedMethods.size());
			methodsCache.put(key, annotatedMethods);
		}
		return annotatedMethods;
	}

	static class Key {
		private final Class<? extends Annotation> methodAnnotation;
		private final Class<?> clazz;

		public Key(Class<?> clazz, Class<? extends Annotation> methodAnnotation) {
			this.clazz = clazz;
			this.methodAnnotation = methodAnnotation;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((clazz == null) ? 0 : clazz.hashCode());
			result = prime * result + ((methodAnnotation == null) ? 0 : methodAnnotation.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			Key other = (Key) obj;
			if (clazz == null) {
				if (other.clazz != null)
					return false;
			} else if (!clazz.equals(other.clazz))
				return false;
			if (methodAnnotation == null) {
				if (other.methodAnnotation != null)
					return false;
			} else if (!methodAnnotation.equals(other.methodAnnotation))
				return false;
			return true;
		}

		@Override
		public String toString() {
			return "Key [methodAnnotation=" + methodAnnotation + ", clazz=" + clazz + "]";
		}
	}

}
